基于SSM的小众书评网 使用了freemarker,bootstrap等等

在这里插入图片描述

小众网

1.使用到的技术有 MyBatis-Plus,Spring-mvc, freemarker

项目的结构图如下: 在这里插入图片描述

web.xm文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext*.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--中文乱码-->
    <filter>
        <filter-name>filter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"

       xsi:schemaLocation="

       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd">


    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="maxActive" value="${jdbc.maxActive}"/>
        <property name="initialSize" value="${jdbc.initialSize}"/>
    </bean>

   <!-- <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> -->
    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="typeAliasesPackage" value="com.pro.domain"/>

    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.pro.mapper"/>
    </bean>


    <!--扫描-->
    <context:component-scan base-package="com.pro"/>
    <mvc:annotation-driven>
        <!--设置相应的编码-->
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=utf-8</value>
                        <value>application/json;charset=utf-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    <!--对于静态资源的访问使用tomcat中的配置-->
    <mvc:default-servlet-handler/>

    <!--配置fmk的模板引擎-->
    <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
        <property name="templateLoaderPath" value="/WEB-INF/ftl"/>
        <property name="freemarkerSettings">
            <props>
                <!--UTF-8-->
                <prop key="defaultEncoding">UTF-8</prop>
            </props>
        </property>
    </bean>

    <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
        <!--视图解析器,将试图和模板结合在一起 差生新的html片段 -->
        <!--设置相应编码-->
        <property name="contentType" value="text/html;charset=utf-8"/>
        <property name="suffix" value=".ftl"/>

    </bean>


    <!--    声明式事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!--配置 kaptcha-->
    <bean id="defaultKaptcha" class="com.google.code.kaptcha.impl.DefaultKaptcha">
        <property name="config">
            <bean class="com.google.code.kaptcha.util.Config">
                <constructor-arg>
                    <props>
                        <prop key="kaptcha.border">no</prop><!--边框-->
                        <prop key="kaptcha.image.width">120</prop><!--宽度120px-->
                        <prop key="kaptcha.textproducer.font.color">blue</prop><!--color-->
                        <prop key="kaptcha.textproducer.font.size">40</prop><!--字大小-->
                        <prop key="kaptcha.textproducer.char.length">4</prop><!--几个字符-->
                    </props>
                </constructor-arg>
            </bean>
        </property>
    </bean>

  <!--  <bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/html/"/>
        <property name="suffix" value=".html"/>
    </bean>
-->
</beans>

mybatis-config.xml 要引入苞米豆的设置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <plugins>
        <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"/>
    </plugins>
</configuration>

jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/meituan
jdbc.username=root
jdbc.password=123456
jdbc.initialSize=5
jdbc.maxActive=20

pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>web2</groupId>
        <artifactId>ssm</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <packaging>war</packaging>
    <artifactId>xzw</artifactId>

    <properties>
        <maven.compiler.source>18</maven.compiler.source>
        <maven.compiler.target>18</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.9</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.9</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.9</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.18</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!--加入依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.23</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.14</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.18</version>
        </dependency>
        <!--模版语言-->
        <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.30</version>
        </dependency>
        <!--spring 支持fk-->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>5.3.18</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.7</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>3.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.jetbrains</groupId>
            <artifactId>annotations</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.github.penggle/kaptcha -->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>

        <!--加密解密组件-->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.3</version>
        </dependency>
    </dependencies>


</project>

数据库表:

/*
 Navicat Premium Data Transfer

 Source Server         : xy
 Source Server Type    : MySQL
 Source Server Version : 80032
 Source Host           : localhost:3306
 Source Schema         : meituan

 Target Server Type    : MySQL
 Target Server Version : 80032
 File Encoding         : 65001

 Date: 21/10/2023 09:30:29
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for book
-- ----------------------------
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book`  (
  `book_id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
  `book_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '书名',
  `sub_title` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '子标题',
  `author` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '作者',
  `cover` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '封面图URL',
  `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '图书描述',
  `category_id` bigint NOT NULL COMMENT '类别',
  `evaluation_score` float(255, 1) NOT NULL DEFAULT 0.0 COMMENT '图书评分',
  `evaluation_quantity` int NOT NULL DEFAULT 0 COMMENT '评价量',
  PRIMARY KEY (`book_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of book
-- ----------------------------
INSERT INTO `book` VALUES (1, '可可私房菜', '私房菜', '可可', 'ccsfc.jpg', '好吃不贵买一送一', 3, 4.0, 110);
INSERT INTO `book` VALUES (2, '经典美味家常菜', '家常菜', '今年', 'jdmwjcc.jpg', '经典', 3, 3.0, 60);
INSERT INTO `book` VALUES (3, '萝卜白菜', '白菜', '萝卜', 'lbbc.jpg', '清香淡雅', 3, 1.0, 20);
INSERT INTO `book` VALUES (4, '肉菜真好吃', '美味', '少华', 'rchhc.jpg', '肉食爱好者的福音,牛羊猪、鸡鸭鱼,一网打尽,每道菜都足够让你大显身手。有连厨房新手也能轻松搞定的,吃起来很过瘾的“酸汤肥牛”;有改良后不油不腻,好吃到停不下来的“春笋五花肉”;有挑食的朋友也吃不够的“黑椒排骨”;有端上桌就能立马吸引你筷子去向的“鲍鱼烧鸡”“蒜蓉胡椒虾球”;还有吃完意犹未尽,还得舔舔手指头的“香辣花螺”。你可一定要试试看啊。', 3, 4.9, 666);
INSERT INTO `book` VALUES (5, '水果切雕', '水果', '水果', 'sgqdbp.jpg', '解渴', 3, 4.5, 333);
INSERT INTO `book` VALUES (6, '烧烤宝典', '烧烤', '烧烤', 'skbd.jpg', '淄博烧烤', 3, 4.5, 345);
INSERT INTO `book` VALUES (7, '蔬菜精做', '蔬菜', '蔬菜', 'swzzz.jpg', '蔬菜', 3, 0.0, 0);
INSERT INTO `book` VALUES (8, 'E xcel', '工作助手', '工作助手', '1.jpg', '工作助手', 4, 0.0, 0);

-- ----------------------------
-- Table structure for category
-- ----------------------------
DROP TABLE IF EXISTS `category`;
CREATE TABLE `category`  (
  `category_id` bigint NOT NULL AUTO_INCREMENT COMMENT '类编号',
  `category_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '书分类',
  PRIMARY KEY (`category_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of category
-- ----------------------------
INSERT INTO `category` VALUES (1, '文学');
INSERT INTO `category` VALUES (2, '历史');
INSERT INTO `category` VALUES (3, '美食');
INSERT INTO `category` VALUES (4, '艺术');

-- ----------------------------
-- Table structure for evaluation
-- ----------------------------
DROP TABLE IF EXISTS `evaluation`;
CREATE TABLE `evaluation`  (
  `evaluation_id` bigint NOT NULL AUTO_INCREMENT COMMENT '评价编号',
  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '短评内容',
  `score` int NOT NULL COMMENT '评分-5分制',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `member_id` bigint NOT NULL COMMENT '会员编号',
  `book_id` bigint NOT NULL COMMENT '图书编号',
  `enjoy` int NOT NULL DEFAULT 0 COMMENT '点赞数量',
  `state` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'enable' COMMENT '审核状态 enable-有效 disable-已禁用',
  `disable_reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '禁用理由',
  `disable_time` datetime NULL DEFAULT NULL COMMENT '禁用时间',
  PRIMARY KEY (`evaluation_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of evaluation
-- ----------------------------
INSERT INTO `evaluation` VALUES (1, ' 只是心口有点堵,看不到真爱的结尾,看不到幸福圆满的结局,主人公孙少平毅然的走着自己的坚实,不卑不亢,不屈不饶,那些真心的爱没有成为他的幸福,或遗憾,或退避,都是为了爱。一种大义凛然,包容博爱,真正男人的形象。恐怕这就是作者所期望读者所领悟的,一种奋发的精神。\r\n', 5, '2023-10-19 15:57:33', 1, 4, 111, 'enable', '无', '2023-10-19 15:58:18');
INSERT INTO `evaluation` VALUES (2, '肉食爱好者的福音,牛羊猪、鸡鸭鱼,一网打尽,每道菜都足够让你大显身手。有连厨房新手也能轻松搞定的,吃起来很过瘾的“酸汤肥牛”;有改良后不油不腻,好吃到停不下来的“春笋五花肉”;有挑食的朋友也吃不够的“黑椒排骨”;有端上桌就能立马吸引你筷子去向的“鲍鱼烧鸡”“蒜蓉胡椒虾球”;还有吃完意犹未尽,还得舔舔手指头的“香辣花螺”。你可一定要试试看啊。', 4, '2023-10-19 16:10:01', 2, 4, 50, 'enable', '无', '2023-10-19 16:10:21');

-- ----------------------------
-- Table structure for member
-- ----------------------------
DROP TABLE IF EXISTS `member`;
CREATE TABLE `member`  (
  `member_id` bigint NOT NULL AUTO_INCREMENT COMMENT '会员编号',
  `username` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名',
  `password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码',
  `salt` int NOT NULL COMMENT '盐值',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `nickname` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '昵称',
  PRIMARY KEY (`member_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of member
-- ----------------------------
INSERT INTO `member` VALUES (1, 'wzmwzm', '74bc40cf4623aa99c07e7169302acf74', 1713, '2023-10-20 14:20:45', 'wzm');
INSERT INTO `member` VALUES (2, 'wzmwzm1', '47986f935974461014db9d9ac7619374', 1502, '2023-10-20 14:22:35', 'wzm');
INSERT INTO `member` VALUES (6, '123456', '5fe5733e150216839b9f876ba946e097', 1851, '2023-10-20 15:33:16', '123456');

-- ----------------------------
-- Table structure for member_read_state
-- ----------------------------
DROP TABLE IF EXISTS `member_read_state`;
CREATE TABLE `member_read_state`  (
  `rs_id` bigint NOT NULL AUTO_INCREMENT COMMENT '会员阅读状态',
  `book_id` bigint NOT NULL COMMENT '图书编号',
  `member_id` bigint NOT NULL COMMENT '会员编号',
  `read_state` int NOT NULL COMMENT '阅读状态 1-想看 2-看过',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`rs_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of member_read_state
-- ----------------------------

-- ----------------------------
-- Table structure for test
-- ----------------------------
DROP TABLE IF EXISTS `test`;
CREATE TABLE `test`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `content` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of test
-- ----------------------------
INSERT INTO `test` VALUES (1, '测试');
INSERT INTO `test` VALUES (2, '测试');
INSERT INTO `test` VALUES (3, '另一种测试');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户编号',
  `username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名',
  `password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码',
  `salt` int NOT NULL COMMENT '盐值',
  PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------

SET FOREIGN_KEY_CHECKS = 1;

先从MyBatis-Plus 说起

要先建立一个实体类的Mapper 接口类 让这个接口继承BaseMapper<实体类> 然后我们就能用他封装好的方法了,我们就在Service层中注入这个Mapper ,要使用 @Autowired 注解 下面我就详细的说说我的 Service 层

这个是BookService的接口

public interface BookService  {
    //分页
    public IPage<Book> paging(Long categoryId,String order, Integer page,Integer rows);
	//根据书号查书
    public Book getBookById(Long bookId);

}

这个是BookService的接口的实现类 在这个类上我写了一个注解@Service 就是为了能让视图扫描器扫描到这个注解

@Autowired 是将BookMapper 注入到这个实现类中

package com.pro.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pro.domain.Book;
import com.pro.service.BookService;
import com.pro.mapper.BookMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author xy
 * @description 针对表【book】的数据库操作Service实现
 * @createDate 2023-10-18 15:00:08
 */
@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookMapper bookMapper;
	//用好封装好的方法来分页
    @Override
    public IPage<Book> paging(Long categoryId,String order,Integer page, Integer rows) {
        Page<Book> p = new Page<>(page, rows);
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        //类别
        if(categoryId!=null&&categoryId!=-1){
            wrapper.eq("category_id",categoryId);
        }
        //热度或者评分
        if (order!=null){
            if ("quantity".equals(order)){
                wrapper.orderByDesc("evaluation_quantity");
            } else if ("score".equals(order)){
                wrapper.orderByDesc("evaluation_score");
            }
        }
        IPage<Book> pageObject = bookMapper.selectPage(p, wrapper);
        return pageObject;
    }

    @Override
    public Book getBookById(Long bookId) {
        Book book = bookMapper.selectById(bookId);
        return book;
    }
}

下面就是 BookController 顾名思义Controller 所以要在类上加上一个@Controller注解

package com.pro.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.pro.domain.Book;
import com.pro.domain.Category;
import com.pro.domain.Evaluation;
import com.pro.service.BookService;
import com.pro.service.CategoryService;
import com.pro.service.EvaluationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@Controller
public class BookController {
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private BookService bookService;
    @Autowired
    private EvaluationService evaluationService;
	
    @GetMapping("/")
    public ModelAndView showIndex() {
        ModelAndView modelAndView = new ModelAndView("/index");
        List<Category> categories = categoryService.selectAll();
        modelAndView.addObject("list", categories);
        return modelAndView;
    }

    @GetMapping("/books")
    @ResponseBody
    public IPage<Book> selectBooks(Long categoryId, String order, Integer page) {
        if (page == null) {
            page = 1;
        }
        IPage<Book> pageObj = bookService.paging(categoryId, order, page, 2);

        return pageObj;

    }
    //查单个书
    @GetMapping("/book/{bookId}")
    public ModelAndView displayBookDetail(@PathVariable("bookId") Long bookId) {
       //得到了书的详细信息
        Book book = bookService.getBookById(bookId);
        //得到评论列表
        List<Evaluation> evlist = evaluationService.getEvaluationByBookId(bookId);
        ModelAndView mav = new ModelAndView("/detail");
        mav.addObject("book", book);
        mav.addObject("evList",evlist);
        return mav;
    }
}

这个是会员登录和注册 Service

1.注册就是根据传入的username 查寻数据库中是否有这个用户,有的话就直接抛出异常 验证码操作是在控制层进行的 ,颜值的话就是随机生成一个随机数加到传入的密码中然后再进行 MD5加密 是这个密码难以被破解。

2.登录根注册的道理差不多,通过查询是否有这个用户名,把这个用户查出来后,在进行密码的比对。

package com.pro.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.pro.domain.Member;
import com.pro.mapper.MemberMapper;
import com.pro.service.exception.ServiceException;
import com.pro.util.MD5Util;
import com.pro.util.ResultEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Random;

@Service
public class MemberServiceImpl implements MemberService {
    @Autowired
    private MemberMapper memberMapper;

    @Override
    public Member createMember(String username, String password, String nickName) {
        QueryWrapper <Member>wrapper=new QueryWrapper<>();
        wrapper.eq("username",username);
        List <Member> list = memberMapper.selectList(wrapper);
        if (!list.isEmpty()){
            //表示已经有了这个会员
            throw  new ServiceException(ResultEnum.USER_NAME_REGISTERED);
        }
        //随机生成盐
        int salt = new Random().nextInt(1000) + 1000;
        String md5pwd = MD5Util.md5Digest(password, salt);
        Member member = new Member();
        member.setUsername(username);
        member.setNickname(nickName);
        member.setSalt(salt);
        member.setPassword(md5pwd);
        member.setCreateTime(new Date());
        memberMapper.insert(member);
        return member;
    }

    @Override
    public Member checkLogin(String username, String password) {
        QueryWrapper<Member> Wrapper = new QueryWrapper<>();
        Wrapper.eq("username",username);
        Member member = memberMapper.selectOne(Wrapper);
        if (member==null){
            throw new ServiceException(ResultEnum.USERNAME_NONE);
        }
        String md5pwd = MD5Util.md5Digest(password, member.getSalt());
        if (!md5pwd.equals(member.getPassword())){
            throw new ServiceException(ResultEnum.PASSWORD_NOT_TRUE);
        }
        return member;
    }
}

接下来就是会员的控制层了

但是要先说一下ModelAndView

一、ModelAndView概述
简单理解它是将后台返回的数据传递给View层,同时包含一个要访问的View层的URL地址。当控制器处理完请求后,通常控制器会将包含视图名称以及一些模型属性的ModelAndView对象返回给DispatcherServlet。

二、ModelAndView的方法
常用ModelAndView方法:

addObject():添加模型数据用
setViewName():设置视图
ModelAndView 对象有两个个作用:

  1. 将底层获取的数据进行封装

将控制器方法中处理的结果数据传递到结果页面,也就是把在结果页面上需要的数据放到ModelAndView对象中即可。

通过key/value的方式绑定数据
modelAndView.addObject(String attributeName, Object attributeValue);

设置跳转地址

ModelAndView view = new ModelAndView("product");//或
modelAndView.setViewName("product");

springMVC.xml中的配置

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<!-- 给view返回的指定页面名称添加前后缀 -->
	<property name="prefix" value="/WEB-INF/views/" />
	<property name="suffix" value=".jsp" />
</bean>

下面是会员控制层

注入了

@Autowired

private MemberService memberService;

从HttpServletRequest request 中获取到验证码的值 方法是 :

String verifyCode = (String) request.getSession().getAttribute(“verifyCode”); 然后就与用户输入的验证码作比较 相同就进行注册或者登录

HttpSession session 把用户放到session中发送到前端以供使用


package com.pro.controller;

import com.pro.domain.Member;
import com.pro.service.MemberService;
import com.pro.service.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;

@Controller
public class MemberController {

    @Autowired
    private MemberService memberService;
	//a标签请求register.html 时会跳转到/reg a标签的请求类型是get
    @GetMapping("/register.html")
    public ModelAndView genRegister() {
        return new ModelAndView("/reg");
    }

    @PostMapping("/regist")
    @ResponseBody
    public Map register(String vc, String username, String password, String nickname, HttpServletRequest request) {
        Map<String, String> r = new HashMap<>();
        //从session 拿到验证码进行验证
        String verifyCode = (String) request.getSession().getAttribute("verifyCode");
        if (vc == null || verifyCode == null || !vc.equalsIgnoreCase(verifyCode)) {
            r.put("code", "900");
            r.put("msg", "验证码错误");
        } else {
            //验证成功后
            try {
                Member member = memberService.createMember(username, password, nickname);
                r.put("code", "200");
                r.put("msg", "register ok !");

            } catch (ServiceException e) {
                r.put("msg", e.getMsg());
                r.put("code", e.getCode());
            }

        }
        return r;
    }

    //去登录页面
    @GetMapping("/login.html")
    public ModelAndView toLogin(){
        return new ModelAndView("/login");
    }

    @PostMapping("/checkLogin")
    @ResponseBody
    public Map checkLogin(String username, String password, String vc, HttpSession session){
        String verifyCode = (String) session.getAttribute("verifyCode");
        Map<String,String> r=new HashMap<>();
        if (vc==null||verifyCode==null||!vc.equalsIgnoreCase(vc)){
            r.put("code","901");
            r.put("msg","验证码错误");
        }
        else {
            try {
                Member member = memberService.checkLogin(username, password);
                session.setAttribute("member",member);
                r.put("code","200");
                r.put("msg","登录成功");
            } catch (ServiceException e) {
                r.put("code",e.getCode());
                r.put("msg",e.getMsg());
            }
        }
        return r;
    }
}

下面就是书的评价的获取方法

EvaluationServiceImpl 类中,主要完成了以下工作:

  1. 注入了 EvaluationMapperMemberMapperBookMapper,使得可以通过这些 mapper 访问数据库。
  2. getEvaluationByBookId 方法中,根据给定的 bookId 查询符合条件的评价列表。首先创建了一个 QueryWrapper 对象,使用 eq 方法设置了查询条件 book_id = bookIdstate = "enable",并按照 create_time 字段降序排列。然后通过 bookMapper 根据 bookId 查询对应的 Book 对象。
  3. 执行查询操作,将查询结果存放在 list 中。接着通过循环遍历 list,并根据每个评价对象的 memberId 查询对应的 Member 对象,并将查询到的 BookMember 对象分别设置到评价对象中。
  4. 最后返回装有所有评价对象的 list

代码的作用是根据给定的 bookId 查询对应书籍的评价列表,并将每个评价对象与对应的书籍和会员进行关联。

package com.pro.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.pro.domain.Book;
import com.pro.domain.Evaluation;
import com.pro.domain.Member;
import com.pro.mapper.BookMapper;
import com.pro.mapper.EvaluationMapper;
import com.pro.mapper.MemberMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class EvaluationServiceImpl implements EvaluationService {
    @Autowired
    private EvaluationMapper evaluationMapper;
    @Autowired
    private MemberMapper memberMapper;

    @Autowired
    private BookMapper bookMapper;

    //评论列表
    @Override
    public List<Evaluation> getEvaluationByBookId(Long bookId) {
        QueryWrapper<Evaluation> wrapper = new QueryWrapper<>();
        wrapper.eq("book_id", bookId);//where id=?
        wrapper.eq("state", "enable");
        wrapper.orderByDesc("create_time");

        Book book = bookMapper.selectById(bookId);
        List<Evaluation> list = evaluationMapper.selectList(wrapper);
        for (Evaluation evaluation : list) {

            Member member = memberMapper.selectById(evaluation.getMemberId());
            evaluation.setBook(book);
            evaluation.setMember(member);
        }
        //循环过后评论集合中的对象就都有了
        return list;
    }
}

前端

这是从category 集合便利 分类列表 列表就如下图
在这里插入图片描述

<#list list as category>
    <a style="cursor: pointer" data-category="${category.categoryId}"
       class="text-black-50 font-weight-bold category">${category.categoryName}</a>
<#--判断是否含有下一个元素如果有则加上-->
    <#if category_has_next>|</#if>
</#list>

定义 art 模板

<#--定义 art 模板-->
<script type="text/html" id="tp1">
    <a href="book/{{bookId}}" style="color: inherit">
        <div class="row mt-2 book">
            <div class="col-4 mb-2 pr-2">
                <img class="img-fluid" src="./resources/images/{{cover}}">
            </div>
            <div class="col-8  mb-2 pl-0">
                <h5 class="text-truncate">{{bookName}}</h5>
                <div class="mb-2 bg-light small  p-2 w-100 text-truncate">{{author}}</div>
                <div class="mb-2 w-100">{{subTitle}}</div>
                <p>
                    <span class="stars" data-score="{{evaluationScore}}" title="gorgeous">

                    </span>
                    <span class="mt-2 ml-2">{{evaluationScore}}</span>
                    <span class="mt-2 ml-2">{{evaluationQuantity}}已评</span>
                </p>
            </div>
        </div>
    </a>
</script>

在这里插入图片描述

这是登录页面的js

<script type="text/javascript">
    /*初始化星星图片路径*/
    $.fn.raty.defaults.path="./resources/raty/lib/images";

    $(function () {
        loadNext(true);

        $("#btnMore").click(function () {
            loadNext();
        });
        //类别高亮
        $(".category").click(function () {
            $(".category").removeClass("highlight");
            $(".category").addClass("text-black-50");
            $(this).addClass("highlight");
            //设置类别   <input type="hidden" id="categoryId" value="-1"> 的值
            //data("category")就是他得知
            let categoryId = $(this).data("category");
            $("#categoryId").val(categoryId);
            loadNext(true);

        });
        //热度和评分高亮
        $(".order").click(function () {
            $(".order").removeClass("highlight");
            $(".order").addClass("text-black-50");
            $(this).addClass("highlight");

            let order = $(this).data("order");
            $("#order").val(order);
            loadNext(true);
        });
    });

    function loadNext(isFirst){
        //如果是初次加载,isFirst 为true
        if (isFirst==true){
            $("#bookList").html("");
            $("#nextPage").val(1);
        }
        //将按钮单击事件内的代码放到这里
        let nextPage = $("#nextPage").val();
        let categoryId = $("#categoryId").val();
        let order = $("#order").val();
        $.ajax({
            url: 'books',
            data:{"page":nextPage,"categoryId":categoryId,"order":order},
            type: 'get',
            dataType: 'json',
            success: function (info) {
                console.log(info);
                let bookList = info.records;
                for (const book of bookList) {
                    // var html = "<li>" + book.bookName + "</li>"
                    let html = template('tp1', book);
                    $("#bookList").append(html);//追加图书列表
                }

                $(".stars").raty({readOnly:true});
                if (info.current<info.pages){
                    $("#nextPage").val(parseInt(info.current)+1);//把当前页码加一
                    $("#btnMore").show();
                    $("#divNoMore").hide();//让div隐藏
                }else {
                    $("#btnMore").hide();
                    $("#divNoMore").show();//显示
                }
            }
        })
    }
</script>

登录的 js

<script>
    function reloadVerifyCode() {
        $("#imgVerifyCode").attr("src", "verify_code?tp=" + new Date().getTime());//时间戳骗过缓存
    }

    $(function () {
        $("#imgVerifyCode").click(function () {//点击验证码时的事件
            reloadVerifyCode();// 刷新验证码
        });
        $("#btnSubmit").click(function () {
            $.ajax({
                url:'checkLogin',
                type:'post',
                dataType:'json',
                data:$("#frmLogin").serialize(),//序列化表单曁获取数据
                success:function (data) {
                    console.log(data)
                    if (data.code=="200"){
                       window.location='/xzw?ts='+new Date().getTime();
                    }
                }
            });
        });
    });
</script>

注册的js

<script type="text/javascript">
    function reloadVerifyCode() {
        $("#imgVerifyCode").attr("src", "verify_code?tp=" + new Date().getTime());//时间戳骗过缓存
    }

    $(function () {
        $("#imgVerifyCode").click(function () {
            reloadVerifyCode();
        });

        $("#btnSubmit").click(function () {
            //进行验证
            let username = $.trim($("#username").val());

            let regex = /^.{6,10}$/; //正则表达式
            if (!regex.test(username)) {
                alert("用户名请输入正确格式(6-10)位 ");
                return
            }
            let password = $.trim($("#password").val());//去掉空格
            if (!regex.test(password)) {
                alert("密码请输入正确格式(6-10)位 ");
                return
            }
            //拿到当前按钮的对象
            let $btnReg = $(this);
            $btnReg.text("正在处理...");
            $btnReg.attr("disabled", "disabled");
            $.ajax({
                url: 'regist',
                type: 'post',
                dataType: 'json',
                data: $("#frmLogin").serialize(),
                success: function (data) {
                    console.log("服务器相应", data);
                    if (data.code == 200) {
                        $("#exampleModalCenter").modal({});
                        $("#exampleModalCenter").modal("show");

                    } else {
                        reloadVerifyCode();
                        $btnReg.text("注   册")
                        $btnReg.attr("disabled", false);
                        alert(data.code+","+data.msg)
                    }
                }
            });
        });
    });

</script>

获取短评 通过evList as evaluation

<div class="reply pl-2 pr-2">
    <#list evList as evaluation>
        <div>
            <div>
                <span class="pt-1 small text-black-50 mr-2">${evaluation.createTime?string('MM-dd')}</span><!-- 日期 -->
                <span class="mr-2 small pt-1">${evaluation.member.nickname}</span><!-- 会员名-->
                <span class="stars mr-2" data-score="${evaluation.score}"></span><!-- 评分 -->
                <!-- 评论编号 -->
                <button type="button" data-evaluation-id="${evaluation.evaluationId}"
                        class="btn btn-success btn-sm text-white float-right" style="margin-top: -3px;">
                    <img style="width: 24px;margin-top: -5px;" 
                         class="mr-1"        src="https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg3.doubanio.com%2Ff%2Ftalion%2F7a0756b3b6e67b59ea88653bc0cfa14f61ff219d%2Fpics%2Fcard%2Fic_like_gray.svg&pos_id=img-RVGXTkg8-1697856670430)"/>
                    <span>${evaluation.enjoy}</span><!-- 点赞数量 -->
                </button>
            </div><!-- 短评内容-->
            <div class="row mt-2 small mb-3">
                ${evaluation.content}
            </div>
            <hr/>
        </div>
        <!-- 所有评论。循环遍历--->
    </#list>
</div>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值