首页 > 数据库 >Spring Boot、MongoDB、Vue 2和Nginx实现一个小说网站

Spring Boot、MongoDB、Vue 2和Nginx实现一个小说网站

时间:2024-06-08 21:05:40浏览次数:27  
标签:vue name Spring Boot springframework Vue import org

在本篇文章中,我们将带你逐步实现一个完备的小说网站项目,技术栈包括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. 后端开发

数据准备:可以去 https://blog.csdn.net/iku_n/article/details/139509931 这里有爬虫的代码,我使用的是改版,并且是直接把数据导入到mongodb里面

首先,我们创建Spring Boot项目,并添加相关依赖。

4.1 创建Spring Boot项目

使用Spring Initializr创建项目

注意选择Java和Maven

4.2 引入pom文件

这里直接复制粘贴我的就行 (高手无视即可)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot</name>
    <description>springboot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.20</version>
        </dependency>
        <!-- poi依赖 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.5</version>
        </dependency>
        <!-- 引入mongodb-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <!--swagger-->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
            <version>2.1.0</version>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--json依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
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<String, String>> 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<Novel> getNovels() {
        // 1.生成七个随机数,随机获取7本书
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.sample(7)
        );
        return mongoTemplate.aggregate(aggregation, "novels", Novel.class).getMappedResults();

    }

    public Page<Novel> 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<Novel> 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<Novel> getNovels() {
        return novelService.getNovels();
    }

    @GetMapping("/first")
    public Page<Novel>  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
<template>
    <div id="app">
        <router-view/>
    </div>
</template>
5.3 开始写页面

先启动vue试试,记得是在vue的目录下

npm run serve

然后在components创建 CommonPage.vue文件

通用组件: CommonPage
<template>
    <div class="card-container">
        <el-card class="centered-card">
            <el-table :data="tableData" stripe :header-cell-style="{backgroundColor: 'aliceblue'}"
                      @selection-change="handleSelectionChange">
                <el-table-column label="书名" prop="title" align="center"></el-table-column>
                <el-table-column label="作者" prop="author" align="center"></el-table-column>
                <el-table-column label="更新时间" prop="updateTime" align="center"></el-table-column>
            </el-table>
            <div class="block" style="margin: 10px 0">
                <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
                               :current-page="pageNum" :page-sizes="[5, 10, 15, 20]" :page-size="pageSize"
                               layout="total, sizes, prev, pager, next, jumper" :total="total">
                </el-pagination>
            </div>
        </el-card>
    </div>
</template>

<script>
export default {
    props: {
        type: {
            type: String,
            required: true
        },
        fetchUrl: {
            type: String,
            required: true
        }
    },
    data() {
        return {
            tableData: [],
            pageNum: 1,
            pageSize: 10,
            total: 0,
        }
    },
    created() {
        this.load();
    },
    watch: {
        type() {
            this.load();
        },
        fetchUrl() {
            this.load();
        }
    },
    methods: {
        load() {
            this.$request.get(this.fetchUrl, {
                params: {
                    type: this.type,
                    page: this.pageNum,
                    size: this.pageSize
                }
            }).then(res => {
                this.tableData = res.content;
                this.total = res.totalElements;
            }).catch(error => {
                console.error('API request error:', error);
                this.tableData = []; // 或根据需求初始化数据
                this.total = 0;
            });
        },
        handleSelectionChange(selection) {
            console.log('Selection changed:', selection);
        },
        handleCurrentChange(pageNum) {
            this.pageNum = pageNum;
            this.load();
        },
        handleSizeChange(pageSize) {
            this.pageSize = pageSize;
            this.load();
        }
    }
}
</script>

<style scoped>
.card-container {
    display: flex;
    justify-content: center;
    text-align: center;
}

.centered-card {
    width: 50%;
    margin-top: 5%;
}
</style>

然后在views目录下

创建HomeView.vue和manager目录

父组件:HomeView
<template>
    <div>
        <el-container>
            <el-header class="nav-bar">
                <div class="header-search">
                    <!-- Your search box and login scripts here -->
                    <div class="search">
                        <form method="get" target="_blank" action="/modules/article/search.php">
                            <el-input style="width: 20%;margin-right: 1%;" placeholder="输入少字也别输入错字"
                                      type="text"></el-input>
                            <el-button type="primary" icon="el-icon-search"></el-button>
                        </form>
                    </div>
                </div>
                <div style="display: flex; justify-content: center; align-items: center">
                    <el-tabs v-model="activeName" @tab-click="handleClick" class="nav_cont">
                        <el-tab-pane label="首页" name="homePage"></el-tab-pane>
                        <el-tab-pane label="玄幻小说" name="first"></el-tab-pane>
                        <el-tab-pane label="修真小说" name="second"></el-tab-pane>
                        <el-tab-pane label="都市小说" name="third"></el-tab-pane>
                        <el-tab-pane label="历史小说" name="fourth"></el-tab-pane>
                        <el-tab-pane label="网游小说" name="wangyou"></el-tab-pane>
                        <el-tab-pane label="科幻小说" name="scienceFiction"></el-tab-pane>
                    </el-tabs>
                </div>
            </el-header>
            <el-main>
                <router-view></router-view>
            </el-main>
        </el-container>
    </div>
</template>

<script>
export default {
    name: 'HomeView',
    data() {
        return {
            activeName: this.$route.name
        };
    },
    watch: {
        $route(to) {
            this.activeName = to.name;
        }
    },
    methods: {
        handleClick(tab) {
            if (tab.name !== this.$route.name) {
                this.$router.push({ name: tab.name });
            }
        }
    },
    mounted() {
        this.activeName = this.$route.name;
    }
}
</script>

<style scoped>
.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; /* 修改活动标签页文字颜色 */
}
</style>
子组件:HomePage(首页)
<template>
    <div class="warpper">
        <div class="cont">
            <div class="left_cont">
                <div class="ls_tit">
                    <h3>站长推荐</h3>
                    <div class="clear"></div>
                </div>
               <el-card style="display: flex;justify-items: center;align-items: center">
                   <div class="ls_box" style="width: 70%">
                       <div class="shu_box1" v-for="novel in novels" :key="novel.title">
                           <p class="p_img">
                               <a :href="novel.link" :title="novel.title">
                                   <img :src="novel.imgUrl" :alt="novel.title" style="width: 110px;height: 130px">
                               </a>
                           </p>
                           <p class="line20"><a :href="novel.link" :title="novel.title">{{ novel.title }}</a></p>
                           <p class="line20">作者:{{ novel.author }}</p>
                           <p class="line20">{{ novel.updateTime }}</p>
                       </div>
                   </div>
               </el-card>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            novels: [
                /* Example of novels array */
                {
                    title: '神印王座II皓月当空',
                    link: 'http://www.biqule.net/book/59265/',
                    imgUrl: 'http://www.biqule.net/files/article/image/59/59263/59263s.jpg',
                    author: '唐家三少',
                    update: '3-9 16:33'
                },
                // Add more novels here...
            ]
        }
    },
    created() {
        this.getNovel()
    },
    methods: {
        getNovel() {
            this.$request.get('/novels').then(res => {
                this.novels = res;
            })
        }
    }
}
</script>

<style scoped>
/* 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;
}
</style>
其他组件:

这些组件变化不大,我这里给出两个,其他的自己补充ok不?

<template>
    <CommonPage fetchUrl="/novels/first" type="科幻小说" />
</template>

<script>
import CommonPage from '@/components/CommonPage.vue';

export default {
    name: 'ScienceFictionTypeNovel',
    components: {
        CommonPage
    }
}
</script>
<template>
    <CommonPage fetchUrl="/novels/first" type="网游小说" />
</template>

<script>
import CommonPage from '@/components/CommonPage.vue';

export default {
    name: 'WangyouTypeNovel',
    components: {
        CommonPage
    }
}
</script>
路由: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,总结

你看看有什么?这什么都没有怎么总结?没有总结散会

标签:vue,name,Spring,Boot,springframework,Vue,import,org
From: https://blog.csdn.net/iku_n/article/details/139520827

相关文章

  • Vue 3 的 <teleport> 组件封装为自定义指令
    <Teleport>是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的DOM结构外层的位置去。https://cn.vuejs.org/guide/built-ins/teleport.html为了更加简便灵活,可以将Vue3的<teleport>组件类似的功能封装为自定义指令。当然,但在某些情况下,直接使用<telepo......
  • VUE3 表单输入绑定
    在前端处理表单时,我们常常需要将表单输入框的内容同步给JavaScript中相应的变量。手动连接值绑定和更改事件监听器可能会很麻烦:template<input:value="text"@input="event=>text=event.target.value">v-model 指令帮我们简化了这一步骤:template<inputv-mod......
  • 【计算机论文指导】基于Spring boot食品安全信息管理系统
    摘要食品安全信息管理系统设计的目的是为用户提供食品信息、科普专栏、食品检测、检测结果、交流论坛等方面的平台。与PC端应用程序相比,食品安全信息管理系统的设计主要面向于用户,旨在为管理员和用户提供一个食品安全信息管理系统。用户可以通过APP及时查看食品信息、新......
  • Springboot 开发 -- Redis实现分布式Session
    一、引言在微服务架构和分布式系统中,会话管理(SessionManagement)成为了一个挑战。传统的基于Servlet容器的会话管理方式在分布式环境下无法有效工作,因为用户请求可能会被分发到不同的服务器上,导致会话数据无法共享。为了解决这个问题,SpringSession提供了一种基于外部存储(......
  • 【计算机毕业设计】springboot027网上点餐系统
    随着科学技术的飞速发展,各行各业都在努力与现代先进技术接轨,通过科技手段提高自身的优势;对于网上点餐系统当然也不能排除在外,随着网络技术的不断成熟,带动了网上点餐系统,它彻底改变了过去传统的管理方式,不仅使服务管理难度变低了,还提升了管理的灵活性。这种个性化的平台特别......
  • 【计算机毕业设计】springboot030甘肃非物质文化网站的设计与开发
    现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本甘肃非物质文化网站就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,......
  • 【计算机毕业设计】springboot031教师工作量管理系统
    随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了教师工作量管理系统的开发全过程。通过分析教师工作量管理系统管理的不足,创建了一个计算机管理教师工作量管理系统的方案。文章介绍了教师工作量管理系统的系统分析部分,包括可......
  • 【计算机毕业设计】springboot032阿博图书馆管理系统
    随着社会的发展,计算机的优势和普及使得阿博图书馆管理系统的开发成为必需。阿博图书馆管理系统主要是借助计算机,通过对图书借阅等信息进行管理。减少管理员的工作,同时也方便广大用户对所需图书借阅信息的及时查询以及管理。阿博图书馆管理系统的开发过程中,采用B/S架构,主......
  • 小白之路之SpringBoot框架
    在最近学习闲暇时刻突然爆发灵感想要自己写一个通用的SpringBoot项目出来,目前使用了到jwt+redis+mybatisPlus嗯差不多这些吧。整个项目的目录框架这个结构差不多也是在网上到处缝缝补补(以致于可能是SpringBoot还是SpringCloud都有一点分不清除了),暂时分为了两个模块1、com......
  • 一文了解 - -> SpringMVC
    一、SpringMVC概述SpringMVC是由Spring官方提供的基于MVC设计理念的web框架。SpringMVC是基于Servlet封装的用于实现MVC控制的框架,实现前端和服务端的交互。1.1SpringMVC优势严格遵守了MVC分层思想采用了松耦合、插件式结构;相比较于我们封装的BaseServlet以及其他的......